package org.biubiu0002.baiblog.core.config;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.biubiu0002.baiblog.common.exception.BizCodeEnumException;
import org.biubiu0002.baiblog.common.utils.R;
import org.biubiu0002.baiblog.core.annotations.PermissionIgnore;
import org.biubiu0002.baiblog.core.localcache.RolePermissionCacheService;
import org.biubiu0002.baiblog.core.model.dto.TokenUserDto;
import org.biubiu0002.baiblog.core.service.AuthService;
import org.biubiu0002.baiblog.core.service.SysUserService;
import org.biubiu0002.baiblog.core.utils.SecurityUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @Author: biubiu0002
 * @Date: 2022-05-07 22:07
 **/
@Component
@Slf4j
public class PermissionInterceptor extends HandlerInterceptorAdapter {

    @Resource
    private AuthService authService;

    @Resource
    private RolePermissionCacheService rolePermissionCacheService;

    @Resource
    private SysUserService sysUserService;

    private static final String CONTENT_TYPE = "application/json;charset=utf-8";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //获取拦截方法信息
        HandlerMethod handlerMethod = null;
        try {
            handlerMethod = (HandlerMethod) handler;

        }catch (Exception e){
            //URL不存在
            this.handlerFindFail(response);
            return false;
        }
        Method method = handlerMethod.getMethod();
        PermissionIgnore permissionIgnore = method.getAnnotation(PermissionIgnore.class);
        if(permissionIgnore!=null){
            //有@PermissionIgnore注解 则放行
            return true;
        }
        //方法全限定名
        String canonicalName = method.getDeclaringClass().getCanonicalName() + "." + method.getName();
        String token = request.getHeader("token");
        if(StringUtils.isBlank(token)){
            //没有登陆
            this.notLoginFail(response);
            return false;
        }
        //先从认证缓存中取用户
        TokenUserDto tokenUser = SecurityUtils.getUser();
        if(tokenUser==null){
            tokenUser=authService.getTokenUser(token);
            //凭证过期
            if(tokenUser==null){
                this.authenticationFail(response);
                return false;
            }
            SecurityUtils.setUser(tokenUser);
        }
        List<String> roles = tokenUser.getRoles();
        Set<String> permissionSet=new HashSet<>();
        for (String role : roles) {
            List<String> permissions = rolePermissionCacheService.loadData(role);
            permissionSet.addAll(permissions);
        }
        if(CollectionUtils.isEmpty(permissionSet)||!permissionSet.contains(canonicalName)){
            this.authorizeFailException(response);
            return false;
        }
        return true;

    }

    private void notLoginFail(HttpServletResponse response) throws IOException {
        response.setContentType(CONTENT_TYPE);
        R r = R.error(BizCodeEnumException.NOT_LOGIN_EXCEPTION);
        PrintWriter pw = response.getWriter();
        pw.write(JSON.toJSONString(r));
        pw.flush();
        pw.close();
    }

    private void authenticationFail(HttpServletResponse response) throws IOException {
        response.setContentType(CONTENT_TYPE);
        R r = R.error(BizCodeEnumException.AUTHENTICATE_FAIL_EXCEPTION);
        PrintWriter pw = response.getWriter();
        pw.write(JSON.toJSONString(r));
        pw.flush();
        pw.close();
    }

    private void authorizeFailException(HttpServletResponse response) throws IOException {
        response.setContentType(CONTENT_TYPE);
        response.setStatus(403);
        R r = R.error(BizCodeEnumException.AUTHORIZE_FAIL_EXCEPTION);
        PrintWriter pw = response.getWriter();
        pw.write(JSON.toJSONString(r));
        pw.flush();
        pw.close();
    }

    private void handlerFindFail(HttpServletResponse response) throws IOException{
        response.setContentType(CONTENT_TYPE);
        response.setStatus(404);
        R r = R.error(BizCodeEnumException.HANDLER_NOT_FOUND_EXCEPTION);
        PrintWriter pw = response.getWriter();
        pw.write(JSON.toJSONString(r));
        pw.flush();
        pw.close();

    }
}
